#include "tree.hpp"
#include "huffman.hpp"

#include <cstring>
#include <string>
#include <fstream>
#include <math.h>
#include <algorithm>

string char_to_bin(int znak)
{
    string bin;
    for(int i = 0; i<8; i++){
        if(znak%2 == 1) bin+= "1";
        else  bin+= "0";
        znak = znak/2;
    }
    reverse(bin.begin(), bin.end());
    return bin;
}

unsigned char bin_to_char(string bin)
{
    int znak=0;
    for(int i =7; i>=0; i--){
        if(bin[i] == '1') znak += pow(2,7-i);
    }
    return (unsigned char)znak;
}

unsigned char bin_to_char(bool bin[])
{
    int znak=0;
    for(int i =7; i>=0; i--){
        if(bin[i] == 1) znak += pow(2,7-i);
    }
    return (unsigned char)znak;
}

string cHuffman::sKoduj(unsigned char znak, cTree *drzewo)
{
    string sciezka= "";
    cNode *szukany;
    szukany = drzewo->Search_znak(drzewo->head, znak);

     if(szukany){
        drzewo->vPath(szukany ,sciezka);
    }else{
        drzewo->vPath(drzewo->nyt, sciezka);
        int i_znak = (int)znak;
        sciezka+= char_to_bin(i_znak);
    }

    drzewo->vAdd_new_node(znak, szukany);

    return sciezka;
}
bool getbit(unsigned char &bufor, int &i, ifstream &plik)
{
    if(i==8){
        bufor = plik.get();
        i = 0;
    }
    if(bufor>127){
        bufor <<= 1;
        i++;
        return 1;
    }else{
        bufor <<= 1;
        i++;
        return 0;
    }
}

void cHuffman::vDekoder(const char *filename_in, const char *filename_out)
{
    ifstream file_in;
    ofstream file_out;
    file_in.open(filename_in,ios::in);
    file_out.open(filename_out,ios::out);
    if(file_out.is_open() && file_in.is_open()){
    cNode *temp;
    cTree drzewo(600);
    int i = 8;
    unsigned char buffor;
    bool znak[8];
    unsigned char cz;
    while(!file_in.eof()){
         temp=drzewo.head;
         while (drzewo.bIs_rodzic(temp))    //przejdź do wierzchołka zewnętrznego
         {
            if(getbit(buffor, i, file_in)){
                temp=temp->Right;
            }
            else{
               temp=temp->Left;
            }
         }
         if(!drzewo.bIs_nyt(temp)) //jeśli nie jest NYTem
         {
             cz = temp->znak;
             file_out.put(temp->znak);
         }
         else{                  //jesli jest nytem

            for(int j=0;j<8;j++){
                znak[j] =getbit(buffor, i, file_in);
            }
            cz = bin_to_char(znak);
            file_out.put(cz);
         }
         drzewo.vAdd_new_node(cz, temp); //aktualizuje drzewo o dany symbol
    }
    file_in.close();
    file_out.close();
    }else
        cout <<"Nie udalo sie otworzyc plikow \n";
}

void cHuffman::vKoder(const char *filename_in, const char *filename_out)
{
    ifstream file_in;
    file_in.open(filename_in,ios::in);
    ofstream file_out(filename_out, ios::binary);

    if(file_out.is_open() && file_in.is_open()){
        cTree drzewo(600);
        unsigned char znak;
        string sBuffer;
        string bytes;
        sBuffer = "";
        char byte;
        while(!file_in.eof()){
            znak = file_in.get();
            sBuffer +=  sKoduj(znak, &drzewo);
            while(sBuffer.length()>=8){
                for(int i = 0; i<8; i++)
                    bytes[i] = sBuffer[i];
                byte= bin_to_char(bytes);
                file_out.put(byte);
               sBuffer.erase(sBuffer.begin(), sBuffer.begin()+8);
            }

        }

    file_out.close();
    file_in.close();
    }
    else
        cout <<"Nie udalo sie otworzyc plikow \n";
}
